package tsing.good_simple.goodutil;

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * Redis分布式锁工具类
 * @author czw
 */
@Component
public class RedissonUtil {
    private static final Logger logger = LoggerFactory.getLogger(RedissonUtil.class);
    /**
     * redis锁前缀
     */
    public static final String SYS_LOCK_FLAG = "KC_LOCK";

    /**
     * 用于隔开缓存前缀与缓存键值
     */
    public static final String KEY_SPLIT = ":";

    /**
     * 静态注入
     */
    private static Redisson redisson;

    @Autowired
    public void setRedisson(Redisson redisson) {
        RedissonUtil.redisson = redisson;
    }


    /**
     * 加锁，无过期时间默认为30s，并且使用watchdog机制会进行自动续期，使用完成之后一定要手动调用unlock方法，不然会产生锁等待，导致积压
     *
     * @param lockName    锁名  相同的key表示相同的锁，建议针对不同的业务使用不同的key
     * @return
     */
    public static boolean getLock(String lockName) {
        String key = getLockKey(lockName);
        //获取锁对象
        RLock lock = redisson.getLock(key);
        logger.info("RedisKey为：{}，成功获取到锁对象：{}",key,lock);
        //加锁
        lock.lock();
        logger.info("获取锁成功，Redis Lock key :{}", key);
        return true;
    }

    /**
     * 加锁，带过期时间，如果程序执行时间可能会超过锁过期时间不建议使用此方法，请结合业务使用
     *
     * @param lockName    锁名  相同的key表示相同的锁，建议针对不同的业务使用不同的key
     * @param expiresTime 过期时间，单位：秒
     * @return
     */
    public static boolean getLock(String lockName, long expiresTime) {
        String key = getLockKey(lockName);
        //获取锁对象
        RLock lock = redisson.getLock(key);
        logger.info(Thread.currentThread().getName()+"RedisKey为：{}，成功获取到锁对象：{}",key,lock);
        //设置锁过期时间
        lock.lock(expiresTime, TimeUnit.SECONDS);
        logger.info(Thread.currentThread().getName()+"获取锁成功，Redis Lock key :{}", key);
        return true;
    }

    /**
     * 释放锁，建议放在 finally里面
     *
     * @param lockName 锁名称
     */
    public static void unlock(String lockName) {
        String key = getLockKey(lockName);
        //获取所对象
        RLock lock = redisson.getLock(key);
        logger.info(Thread.currentThread().getName()+"RedisKey为：{}，成功获取到锁对象：{},准备释放锁",key,lock);
        boolean locked = lock.isLocked();
        boolean heldByCurrentThread = lock.isHeldByCurrentThread();

        // 释放锁,判断要解锁的key是否已被锁定并且是否被当前线程保持
        if (lock.isLocked() && lock.isHeldByCurrentThread()) {
            lock.unlock();
            logger.info(Thread.currentThread().getName()+"释放Redis锁成功，key:{}", key);
        }
    }

    /**
     * 对锁的key添加系统标识前缀
     *
     * @return
     */
    private static String getLockKey(String key) {
        return RedissonUtil.SYS_LOCK_FLAG + RedissonUtil.KEY_SPLIT + key;
    }


    /**
     * 放入Redisson延迟队列，此方案完全基于redis，如果集群宕机数据不一致，恢复困难所以废弃
     */
    /*public static void queue(){
        String queuename = "delay-queue";
        RBlockingQueue<String> blockingQueue = redisson.getBlockingQueue(queuename);
        RDelayedQueue<String> delayedQueue = redisson.getDelayedQueue(blockingQueue);
        delayedQueue.offer("测试延迟消息10", 10, TimeUnit.SECONDS);
        delayedQueue.offer("测试延迟消息15", 15, TimeUnit.SECONDS);
        delayedQueue.offer("测试延迟消息8", 8, TimeUnit.SECONDS);
        delayedQueue.offer("测试延迟消息20", 20, TimeUnit.SECONDS);
    }*/

    /**
     * 消费Redisson延迟队列，此方案完全基于redis，如果集群宕机数据不一致，恢复困难所以废弃
     * @throws InterruptedException
     */
    /*public static void consume() throws InterruptedException {
        while (true) {
            String queuename = "delay-queue";
            RBlockingQueue<String> blockingQueue = redisson.getBlockingQueue(queuename);
            RDelayedQueue<String> delayedQueue = redisson.getDelayedQueue(blockingQueue);
            String msg = blockingQueue.take();
            System.out.println(msg);
        }
    }*/

}
